#include "stdafx.h"
#include "xKernel/IxWndManager.h"
#include "xKernel/IxPaintSchedule.h"
#include "xKernel/IxMsgDispatcher.h"
#include "Wnd.h"

CxWnd::CxWnd()
    : m_spCanvas(NULL)
    , m_spLayoutSchedule(NULL)
    , m_spPaintSchedule(NULL)
{
    ::xCommon::CreateObjectByIID(__uuidof(IxCanvas), (void**)&m_spCanvas);
    ATLASSERT(m_spCanvas);

    ::xCommon::CreateObjectByIID(__uuidof(IxLayoutSchedule), (void**)&m_spLayoutSchedule);
    ATLASSERT(m_spLayoutSchedule);

    ::xCommon::CreateObjectByIID(__uuidof(IxPaintSchedule), (void**)&m_spPaintSchedule);
    ATLASSERT(m_spPaintSchedule);
}

CxWnd::~CxWnd()
{}

BOOL CxWnd::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
{
    SetMsgHandled(FALSE);

    _init();

    return FALSE;
}

void CxWnd::OnPaint(CDCHandle dc)
{
    if (m_spLayoutSchedule)
    {
        m_spLayoutSchedule->Layout();
    }

    RECT rcWnd = {0};
    GetClientRect(&rcWnd);

    PAINTSTRUCT ps = {0};
    BeginPaint(&ps);

    ATLASSERT(m_spCanvas);
    if (m_spCanvas && m_spPaintSchedule)
    {
        m_spPaintSchedule->Paint(m_spCanvas);
        //m_spCanvas->PaintToDC(CPoint(ps.rcPaint.left, ps.rcPaint.top), ps.hdc, ps.rcPaint);
        m_spCanvas->PaintToDC(CPoint(0, 0), ps.hdc, rcWnd);
    }

    EndPaint(&ps);
}

BOOL CxWnd::OnEraseBkgnd(CDCHandle dc)
{
    return 0;
}

void CxWnd::OnMouseMove(UINT nFlags, CPoint point)
{
    SetMsgHandled(FALSE);

    CRect rcClient(0, 0, 0, 0);
    GetClientRect(&rcClient);

    if (rcClient.PtInRect(point))
    {
        typedef std::vector< CComPtr<IxView> > VIEWS;
        VIEWS views;
        views.push_back(GetEntity<IxView>());
        xKernel::GetViewsByPoint(GetEntity<IxView>(), point, views);
    }
}

void CxWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
    SetMsgHandled(FALSE);
}

void CxWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
    SetMsgHandled(FALSE);
}

void CxWnd::OnSize(UINT nType, CSize size)
{
    SetMsgHandled(TRUE);

    DefWindowProc();

    // do not call SetBoxRect or SetSize
    if (SIZE_MINIMIZED != nType)
    {
        if (size != m_rcBox.Size())
        {
            RECT rcWnd = {0, 0, size.cx, size.cy};
            __super::SetBoxRect(rcWnd);

            ScheduleLayout(GetEntity<IxView>());
            SchedulePaint(GetEntity<IxView>());

            _reset_canvas(size);
        }
    }
}

void CxWnd::OnFinalMessage(HWND hWnd)
{
    __super::OnFinalMessage(hWnd);

    _uninit();
}

HRESULT CxWnd::CreateWnd()
{
    Create();
    ShowWindow(SW_SHOW);
    return S_OK;
}

HRESULT CxWnd::GetLayoutSchedule(IxLayoutSchedule** ppLayoutSchedule)
{
    if (!ppLayoutSchedule)
    {
        return E_POINTER;
    }

    *ppLayoutSchedule = NULL;

    if (m_spLayoutSchedule)
    {
        return m_spLayoutSchedule->QueryInterface(__uuidof(IxLayoutSchedule), (void**)ppLayoutSchedule);
    }
    else
    {
        return E_FAIL;
    }
}

// HRESULT CxWnd::Paint(IxCanvas* pCanvas)
// {
//     if (!pCanvas)
//     {
//         return E_POINTER;
//     }
// 
//     COLORREF clr = RGB(200,100,100);
// 
//     clr += (m_byAlpha << 24);
// 
//     pCanvas->FillSolidRect(m_rcBox, clr);
// 
// #if _DEBUG
//     ATLTRACE(L"\n paint times(%d) this(%x) rect(%d, %d, %d, %d)", ++__count, this, m_rcBox.left, m_rcBox.top, m_rcBox.Width(), m_rcBox.Height());
// #endif
// 
//     return S_OK;
// }

HRESULT CxWnd::ProcessMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plRet, LPBOOL pbHandle)
{
    *pbHandle = FALSE;
    return S_OK;
}

HRESULT CxWnd::SetBoxRect(RECT rcBox)
{
    if (0 != rcBox.left || 0 != rcBox.top)
    {
        ATLASSERT(!L"rcBox's top left point must be (0,0)!!!");
        return E_INVALIDARG;
    }

    CRect rcOld = m_rcBox;
    __super::SetBoxRect(rcBox);

    if (rcOld != m_rcBox && IsWindow())
        SetWindowPos(NULL, &rcBox, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);

    return S_OK;
}

HRESULT CxWnd::GetAlpha(LPBYTE pbyAlpha)
{
    // to do ...
    return S_OK;
}

HRESULT CxWnd::SetAlpha(BYTE byAlpha)
{
    // to do ...
    return S_OK;
}

HRESULT CxWnd::GetShowState(LPBOOL pbShow)
{
    if (!pbShow)    return E_POINTER;

    *pbShow = IsWindowVisible();

    return S_OK;
}

HRESULT CxWnd::SetShowState(BOOL bShow)
{
    if (bShow != IsWindowVisible())
    {
        ShowWindow(bShow ? SW_SHOW : SW_HIDE);
    }
    return S_OK;
}

HRESULT CxWnd::GetVisibleState(LPBOOL pbVisible)
{
    return GetShowState(pbVisible);
}

HRESULT CxWnd::SetFocus()
{
    if (GetFocus() != m_hWnd)
    {
        SetFocus();
    }
    return S_OK;
}

HRESULT CxWnd::GetFocusState(LPBOOL pbFocus)
{
    if (!pbFocus)   return E_POINTER;

    *pbFocus = (GetFocus() == m_hWnd);

    return S_OK;
}

HRESULT CxWnd::ScheduleLayout(IxView* pView)
{
    if (!m_spLayoutSchedule)
    {
        return E_FAIL;
    }

    return m_spLayoutSchedule->AddView(pView);
}

HRESULT CxWnd::SchedulePaint(IxView* pView)
{
    if (!m_spPaintSchedule)
    {
        return E_FAIL;
    }

    return m_spPaintSchedule->AddView(pView);
}

HRESULT CxWnd::Invalidate(RECT rcDirty)
{
    if (IsWindow())
        InvalidateRect(&rcDirty, TRUE);
    return S_OK;
}

void CxWnd::_init()
{
    CComPtr<IxWndManager> spWndMgr;
    ::xCommon::GetService(__uuidof(IxWndManager), (void**)&spWndMgr);
    ATLASSERT(spWndMgr);
    if (spWndMgr)
    {
        spWndMgr->AddWnd(GetEntity<IxWnd>());
    }

    ::xCommon::CreateObjectByIID(__uuidof(IxMsgDispatcher), (void**)&m_spMsgDispatcher);
    ATLASSERT(m_spMsgDispatcher);
    if (m_spMsgDispatcher)
    {
        m_spMsgDispatcher->Initialize(GetEntity<IxView>());
    }
}

void CxWnd::_uninit()
{
    CComPtr<IxWndManager> spWndMgr;
    ::xCommon::GetService(__uuidof(IxWndManager), (void**)&spWndMgr);
    ATLASSERT(spWndMgr);
    if (spWndMgr)
    {
        spWndMgr->RemoveWnd(GetEntity<IxWnd>());
    }

    if (m_spMsgDispatcher)
    {
        m_spMsgDispatcher->Uninitialize();
        m_spMsgDispatcher = NULL;
    }
}

void CxWnd::_reset_canvas(const CSize& size)
{
    ATLASSERT(m_spCanvas);
    if (m_spCanvas)
    {
        SIZE sz = {0};
        m_spCanvas->GetSize(&sz);

        if (sz.cx != size.cx || sz.cy != size.cy)
        {
            HDC hdc = GetDC();
            m_spCanvas->Create(hdc, size);
            ReleaseDC(hdc);
        }
    }
}